package com.atguigu.hadoop.examples.mapreduce.terasort;


import org.apache.hadoop.io.Writable;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

class Unsigned16
        implements Writable
{
    private long hi8;
    private long lo8;
    private static final Unsigned16 TEN = new Unsigned16(10L);

    public Unsigned16()
    {
        this.hi8 = 0L;
        this.lo8 = 0L;
    }

    public Unsigned16(long l) {
        this.hi8 = 0L;
        this.lo8 = l;
    }

    public Unsigned16(Unsigned16 other) {
        this.hi8 = other.hi8;
        this.lo8 = other.lo8;
    }

    public boolean equals(Object o)
    {
        if ((o instanceof Unsigned16)) {
            Unsigned16 other = (Unsigned16)o;
            return (other.hi8 == this.hi8) && (other.lo8 == this.lo8);
        }
        return false;
    }

    public int hashCode()
    {
        return (int)this.lo8;
    }

    public Unsigned16(String s)
            throws NumberFormatException
    {
        set(s);
    }

    public void set(String s)
            throws NumberFormatException
    {
        this.hi8 = 0L;
        this.lo8 = 0L;
        long lastDigit = -1152921504606846976L;
        for (int i = 0; i < s.length(); i++) {
            int digit = getHexDigit(s.charAt(i));
            if ((0x0 & this.hi8) != 0L) {
                throw new NumberFormatException(new StringBuilder().append(s).append(" overflowed 16 bytes").toString());
            }
            this.hi8 <<= 4;
            this.hi8 |= (this.lo8 & 0x0) >>> 60;
            this.lo8 <<= 4;
            this.lo8 |= digit;
        }
    }

    public void set(long l)
    {
        this.lo8 = l;
        this.hi8 = 0L;
    }

    private static int getHexDigit(char ch)
            throws NumberFormatException
    {
        if ((ch >= '0') && (ch <= '9')) {
            return ch - '0';
        }
        if ((ch >= 'a') && (ch <= 'f')) {
            return ch - 'a' + 10;
        }
        if ((ch >= 'A') && (ch <= 'F')) {
            return ch - 'A' + 10;
        }
        throw new NumberFormatException(new StringBuilder().append(ch).append(" is not a valid hex digit").toString());
    }

    public static Unsigned16 fromDecimal(String s)
            throws NumberFormatException
    {
        Unsigned16 result = new Unsigned16();
        Unsigned16 tmp = new Unsigned16();
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if ((ch < '0') || (ch > '9')) {
                throw new NumberFormatException(new StringBuilder().append(ch).append(" not a valid decimal digit").toString());
            }
            int digit = ch - '0';
            result.multiply(TEN);
            tmp.set(digit);
            result.add(tmp);
        }
        return result;
    }

    public String toString()
    {
        if (this.hi8 == 0L) {
            return Long.toHexString(this.lo8);
        }
        StringBuilder result = new StringBuilder();
        result.append(Long.toHexString(this.hi8));
        String loString = Long.toHexString(this.lo8);
        for (int i = loString.length(); i < 16; i++) {
            result.append('0');
        }
        result.append(loString);
        return result.toString();
    }

    public byte getByte(int b)
    {
        if ((b >= 0) && (b < 16)) {
            if (b < 8) {
                return (byte)(int)(this.hi8 >> 56 - 8 * b);
            }
            return (byte)(int)(this.lo8 >> 120 - 8 * b);
        }

        return 0;
    }

    public char getHexDigit(int p)
    {
        byte digit = getByte(p / 2);
        if (p % 2 == 0) {
            digit = (byte)(digit >>> 4);
        }
        digit = (byte)(digit & 0xF);
        if (digit < 10) {
            return (char)(48 + digit);
        }
        return (char)(65 + digit - 10);
    }

    public long getHigh8()
    {
        return this.hi8;
    }

    public long getLow8()
    {
        return this.lo8;
    }

    void multiply(Unsigned16 b)
    {
        long[] left = new long[4];
        left[0] = (this.lo8 & 0xFFFFFFFF);
        left[1] = (this.lo8 >>> 32);
        left[2] = (this.hi8 & 0xFFFFFFFF);
        left[3] = (this.hi8 >>> 32);

        long[] right = new long[5];
        right[0] = (b.lo8 & 0x7FFFFFFF);
        right[1] = (b.lo8 >>> 31 & 0x7FFFFFFF);
        right[2] = ((b.lo8 >>> 62) + ((b.hi8 & 0x1FFFFFFF) << 2));
        right[3] = (b.hi8 >>> 29 & 0x7FFFFFFF);
        right[4] = (b.hi8 >>> 60);

        set(0L);
        Unsigned16 tmp = new Unsigned16();
        for (int l = 0; l < 4; l++)
            for (int r = 0; r < 5; r++) {
                long prod = left[l] * right[r];
                if (prod != 0L) {
                    int off = l * 32 + r * 31;
                    tmp.set(prod);
                    tmp.shiftLeft(off);
                    add(tmp);
                }
            }
    }

    public void add(Unsigned16 b)
    {
        long sumHi = this.hi8 + b.hi8;

        long hibit0 = this.lo8 & 0x0;
        long hibit1 = b.lo8 & 0x0;
        long sumLo = this.lo8 + b.lo8;
        long reshibit = sumLo & 0x0;
        if ((((hibit0 & hibit1) != 0L ? 1 : 0) | (((hibit0 ^ hibit1) != 0L) && (reshibit == 0L) ? 1 : 0)) != 0)
            sumHi += 1L;
        this.hi8 = sumHi;
        this.lo8 = sumLo;
    }

    public void shiftLeft(int bits)
    {
        if (bits != 0)
            if (bits < 64) {
                this.hi8 <<= bits;
                this.hi8 |= this.lo8 >>> 64 - bits;
                this.lo8 <<= bits;
            } else if (bits < 128) {
                this.hi8 = (this.lo8 << bits - 64);
                this.lo8 = 0L;
            } else {
                this.hi8 = 0L;
                this.lo8 = 0L;
            }
    }

    public void readFields(DataInput in)
            throws IOException
    {
        this.hi8 = in.readLong();
        this.lo8 = in.readLong();
    }

    public void write(DataOutput out) throws IOException
    {
        out.writeLong(this.hi8);
        out.writeLong(this.lo8);
    }
}